package com.asen.commons.core.jdbc.param;

import com.asen.commons.annotation.FieldBool;
//import com.asen.commons.annotation.FieldSubordinate;
import com.asen.commons.core.jdbc.handler.TypeHandler;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.core.utils.EntityUtils;
import com.asen.commons.utils.BaseAssert;
import com.asen.commons.utils.DecimalUtils;
import com.asen.commons.utils.StringUtils;

import java.math.BigDecimal;

/**
 * SQL会话管理器
 *
 * @author Gary
 * @version 1.0
 * @date 2021-08-21
 */
public class SqlContextManager {

    private static final String MAIN_ALIAS = "_t0";

    private static final String TABLE_ALIAS_PREFIX = "_t";

    private SqlContext context;

    private EntityObjectLoader loader = EntityObjectLoader.getInstance();

    public static SqlContextManager newInstance(SqlContext context) {
        SqlContextManager manager = new SqlContextManager();
        manager.setContext(context);
        return manager;
    }

    public void setValue(FieldObject fo, Object model) throws Exception {
        if (fo.isExtFiled()) {
            return;
        }

        if (fo.isIgnore()) {
            return;
        }

        //if (fo.isFieldSubordinate()) {
        //    return;
        //}

        Class<?> javaType = fo.getJavaType();

        // TODO 暂不支持集合类型
        if (fo.isCollection()) {
            return;
        }

        // 低代码实体跳过
        if (fo.isLCModel()) {
            return;
        }

        String column = fo.getColumn();
        Object value = fo.getValue(model);

        if (BigDecimal.class.isAssignableFrom(fo.getJavaType())
                && fo.isFieldDecimal()
                && value != null) {
            BigDecimal decimal = StringUtils.toDecimal(value);

            int scale = fo.getDecimalScale();
            if (scale < 0) {
                scale = DecimalUtils.getScale(fo.getDecimalTypeEnum());
            }

            if (scale >= 0) {
                decimal = decimal.setScale(scale, BigDecimal.ROUND_HALF_UP);
            }

            if (fo.stripTrailingZeros()) {
                decimal = decimal.stripTrailingZeros();
            }

            value = decimal;
        }

        if (Boolean.class.isAssignableFrom(fo.getJavaType())
                && fo.isFieldBool()
                && value == null) {
            FieldBool fieldBool = fo.getAnnotation(FieldBool.class);
            if (fieldBool.nullToFalse()) {
                value = Boolean.FALSE;
            }
        }

        TypeHandler<?> typeHandler = EntityUtils.getTypeHandler(fo);
        BaseAssert.notNull(typeHandler, "不支持的参数类型【" + javaType.getName() + "】");

        if (value != null) {
            context.setParam(javaType, column, value, typeHandler);
        } else {
            context.setNull(javaType, column, typeHandler);
        }
    }

    public SqlColumn getSqlColumn(String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return null;
        }

        SqlColumn result = context.getSqlColumn(fullName);
        if (result != null) {
            return result;
        }

        int index = fullName.indexOf(".");
        if (index <= 0) {
            FieldObject fo = context.getMainField(fullName);

            // 字段不存在，则忽略
            if (fo == null) {
                return null;
            }

            if (fo.isIgnore() && !fo.isSelectOnly()) {
                return null;
            }

            String columnWithAlias = getColumnWithAlias(MAIN_ALIAS, fo.getColumn());
            return createSqlColumn(fullName, columnWithAlias, MAIN_ALIAS, fo);
        }

        // 复杂属性
        String fieldName = fullName.substring(0, index);
        String subName = fullName.substring(index + 1, fullName.length());

        FieldObject fo = context.getMainField(fieldName);
        if (fo == null) {
            return null;
        }

        if (fo.isIgnore() && !fo.isSelectOnly()) {
            return null;
        }

        // 外部关联属性，仅支持ID属性
        //if (fo.isRemoteReference()) {
        //    if ("id".equals(subName)) {
        //        String columnWithAlias = getColumnWithAlias(MAIN_ALIAS, fo.getColumn());
        //        return createSqlColumn(fullName, columnWithAlias, MAIN_ALIAS, fo);
        //    }
        //    return null;
        //}

        // 数据字典类型
        //if (fo.isDict()) {
        //    String columnWithAlias = getColumnWithAlias(MAIN_ALIAS, fo.getColumn());
        //    return createSqlColumn(fullName, columnWithAlias, MAIN_ALIAS, fo);
        //}

        // 数据字典类型
        //if (fo.isDictItem()) {
        //    String columnWithAlias = getColumnWithAlias(MAIN_ALIAS, fo.getColumn());
        //    return createSqlColumn(fullName, columnWithAlias, MAIN_ALIAS, fo);
        //}

        String joinAlias = getJoinTableAlias(fo, MAIN_ALIAS, "");
        EntityObject subEo = loader.load(fo);
        String prefix = fieldName + ".";

        SqlColumn sqlColumn = getSqlColumn(subEo, subName, joinAlias, prefix);
        if (sqlColumn == null) {
            return null;
        }

        sqlColumn.setFullName(fullName);
        return sqlColumn;
    }

    private SqlColumn getSqlColumn(EntityObject eo, String subName, String joinAlias, String prefix) {
        int index = subName.indexOf(".");
        if (index <= 0) {
            FieldObject fo = eo.getFieldObject(subName);
            if (fo == null) {
                return null;
            }

            if (fo.isIgnore() && !fo.isSelectOnly()) {
                return null;
            }

            String columnWithAlias = getColumnWithAlias(joinAlias, fo.getColumn());
            return createSqlColumn(prefix + subName, columnWithAlias, joinAlias, fo);
        }

        String fieldName = subName.substring(0, index);
        subName = subName.substring(index + 1, subName.length());

        FieldObject subFo = eo.getFieldObject(fieldName);

        if (subFo == null) {
            return null;
        }

        if (subFo.isIgnore() && !subFo.isSelectOnly()) {
            return null;
        }

        // 外部关联属性，仅支持ID属性
        //if (subFo.isRemoteReference()) {
        //    if ("id".equals(subName)) {
        //        String columnWithAlias = getColumnWithAlias(joinAlias, subFo.getColumn());
        //        return createSqlColumn(prefix + subName, columnWithAlias, joinAlias, subFo);
        //    }
        //    return null;
        //}

        // 数据字典类型
        //if (subFo.isDictItem()) {
        //    String columnWithAlias = getColumnWithAlias(joinAlias, subFo.getColumn());
        //    return createSqlColumn(prefix + subName, columnWithAlias, joinAlias, subFo);
        //}

        EntityObject subEo = loader.load(subFo);

        joinAlias = getJoinTableAlias(subFo, joinAlias, prefix);
        prefix = prefix + "." + fieldName;

        return getSqlColumn(subEo, subName, joinAlias, prefix);
    }

    private SqlColumn createSqlColumn(String fullName, String fullColumn, String tableAlias, FieldObject fieldObject) {
        SqlColumn sqlColumn = new SqlColumn(fullName, fullColumn, tableAlias, fieldObject);
        context.putSqlColumn(fullName, sqlColumn);
        return sqlColumn;
    }

    private String getColumnWithAlias(String alias, String column) {
        StringBuilder sb = new StringBuilder();
        sb.append(alias);
        sb.append(".`");
        sb.append(column);
        sb.append("`");
        return sb.toString();
    }

    private String getJoinTableAlias(FieldObject fo, String leftAlias, String prefix) {
        String fieldName = fo.getName();
        String refName = prefix + fieldName;
        String tableAlias = context.getRefAlias(refName);

        if (StringUtils.isEmpty(tableAlias)) {
            tableAlias = leftJoin(fo, leftAlias, prefix);
        }

        return tableAlias;
    }

    private String leftJoin(FieldObject leftFo, String leftAlias, String prefix) {
        String tableName = leftFo.getRefTable();
        String leftColumn = leftFo.getLeftKeyColumn();
        String rightColumn = "id";

        EntityObject leftEo = null;
        //if (StringUtils.isEmpty(tableName)) {
        //    leftEo = loader.load(leftFo);
        //    tableName = leftEo.getTable();
        //}
        //
        //if (leftFo.isFieldSubordinate()) {
        //    FieldSubordinate fieldSubordinate = leftFo.getAnnotation(FieldSubordinate.class);
        //    leftColumn = "id";
        //    String refKey = fieldSubordinate.pidKey();
        //    if (leftEo == null) {
        //        leftEo = loader.load(leftFo.getJavaType());
        //    }
        //    FieldObject rightFo = leftEo.getFieldObject(refKey);
        //    rightColumn = rightFo.getColumn();
        //} else {
        //    String refKey = leftFo.getRefKey();
        //    if (StringUtils.isNotEmpty(refKey)) {
        //        if (leftEo == null) {
        //            leftEo = loader.load(leftFo);
        //        }
        //        FieldObject rightFo = leftEo.getFieldObject(refKey);
        //        rightColumn = rightFo.getColumn();
        //    }
        //}

        BaseAssert.notEmpty(tableName, "表名不能为空");
        BaseAssert.notEmpty(rightColumn, "字段名不能为空");

        String rightAlias = TABLE_ALIAS_PREFIX + context.getTableIndexAndIncrement();

        String refName = prefix + leftFo.getName();
        context.putRefEntity(refName, leftEo);
        context.putRefAlias(refName, rightAlias);

        StringBuilder joinSql = new StringBuilder();
        joinSql.append(" LEFT JOIN `")
                .append(tableName)
                .append("` ")
                .append(rightAlias);
        joinSql.append(" ON ")
                .append(leftAlias)
                .append(".`")
                .append(leftColumn)
                .append("` = ")
                .append(rightAlias)
                .append(".`")
                .append(rightColumn)
                .append("`");
        context.appendJoinSql(joinSql);

        return rightAlias;
    }

    public SqlContext getContext() {
        return context;
    }

    public void setContext(SqlContext context) {
        this.context = context;
    }

    public ParamStatementSetter getParamSetter() {
        return context.getParamSetter();
    }

    public void setParam(Class<?> javaType, Object value) {
        getParamSetter().setParam(javaType, value);
    }
}
